/*
 * SHOPTNT 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.shoptnt.cn
 */
package cn.shoptnt.service.goodssearch.impl;

import cn.shoptnt.client.system.HotkeywordClient;
import cn.shoptnt.framework.database.WebPage;
import cn.shoptnt.framework.elasticsearch.ElasticJestConfig;
import cn.shoptnt.framework.elasticsearch.ElasticOperationUtil;
import cn.shoptnt.framework.elasticsearch.core.ElasticSearchResult;
import cn.shoptnt.framework.util.HexUtils;
import cn.shoptnt.mapper.goods.GoodsWordsMapper;
import cn.shoptnt.model.base.rabbitmq.AmqpExchange;
import cn.shoptnt.client.member.ShopCatClient;
import cn.shoptnt.model.goods.vo.GoodsSearchResultVO;
import cn.shoptnt.model.goodssearch.*;
import cn.shoptnt.model.goods.dos.BrandDO;
import cn.shoptnt.model.goods.dos.CategoryDO;
import cn.shoptnt.model.goods.vo.CategoryVO;
import cn.shoptnt.model.pagedata.HotKeyword;
import cn.shoptnt.service.goods.BrandManager;
import cn.shoptnt.service.goods.CategoryManager;
import cn.shoptnt.service.goods.util.CatUrlUtils;
import cn.shoptnt.service.goods.util.Separator;
import cn.shoptnt.service.goodssearch.GoodsSearchManager;
import cn.shoptnt.service.goodssearch.util.SelectorUtil;
import cn.shoptnt.service.goodssearch.util.SortContainer;
import cn.shoptnt.model.shop.dos.ShopCatDO;
import cn.shoptnt.framework.elasticsearch.EsSettings;
import cn.shoptnt.framework.exception.ServiceException;
import cn.shoptnt.framework.util.StringUtil;
import io.searchbox.client.JestClient;
import io.searchbox.core.search.aggregation.FilterAggregation;
import io.searchbox.core.search.aggregation.MetricAggregation;
import io.searchbox.core.search.aggregation.TermsAggregation;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import cn.shoptnt.framework.rabbitmq.MessageSender;
import cn.shoptnt.framework.rabbitmq.MqMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 基于es的商品检索
 *
 * @author fk
 * @version v6.4
 * @since v6.4
 * 2017年9月18日 上午11:42:06
 */
@Service
public class GoodsSearchManagerImpl implements GoodsSearchManager {

    @Autowired
    protected CategoryManager categoryManager;

    @Autowired
    protected BrandManager brandManager;

    @Autowired
    protected ShopCatClient shopCatClient;

    @Autowired
    private MessageSender messageSender;

    @Autowired
    private GoodsWordsMapper goodsWordsMapper;

    @Autowired
    private HotkeywordClient hotkeywordClient;

    @Autowired
    protected ElasticJestConfig elasticJestConfig;

    @Autowired
    private JestClient jestClient;


    public GoodsSearchManagerImpl() {
        System.setProperty("es.set.netty.runtime.available.processors", "false");
    }

    @Override
    public GoodsSearchResultVO searchGoodsAndSelector(GoodsSearchDTO goodsSearch) {
        //返回结果
        GoodsSearchResultVO goodsSearchResult = new GoodsSearchResultVO();

        Long pageNo = goodsSearch.getPageNo();
        Long pageSize = goodsSearch.getPageSize();

        try {
            SearchSourceBuilder searchSourceBuilder = this.createQuery(goodsSearch);

            //如果不为空 则表示关键词搜索
            if (!StringUtil.isEmpty(goodsSearch.getKeyword())) {
                //搜索关键字消息
                this.messageSender.send(new MqMessage(AmqpExchange.SEARCH_KEYWORDS, AmqpExchange.SEARCH_KEYWORDS + "_ROUTING", goodsSearch.getKeyword()));
            }

            //设置分页信息  设置是否按查询匹配度排序
            searchSourceBuilder.from((pageNo.intValue() - 1) * pageSize.intValue()).size(pageSize.intValue()).explain(true);

            //分类
            AggregationBuilder categoryTermsBuilder = AggregationBuilders.terms("categoryAgg").field("categoryId").size(Integer.MAX_VALUE);
            //品牌
            AggregationBuilder brandTermsBuilder = AggregationBuilders.terms("brandAgg").field("brand").size(Integer.MAX_VALUE);
            //参数
            AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field("params.value").size(Integer.MAX_VALUE);
            AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field("params.name").subAggregation(valuesBuilder).size(Integer.MAX_VALUE);
            AggregationBuilder avgBuild = AggregationBuilders.nested("paramsAgg", "params").subAggregation(paramsNameBuilder);

            searchSourceBuilder.aggregation(categoryTermsBuilder);
            searchSourceBuilder.aggregation(brandTermsBuilder);
            searchSourceBuilder.aggregation(avgBuild);

            // Elasticsearch 搜索
            ElasticSearchResult elasticSearchResult = ElasticOperationUtil.search(jestClient,elasticJestConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME,searchSourceBuilder.toString());

            //商品分页数据
            WebPage webPage = new WebPage(pageNo, elasticSearchResult.getTotal(),pageSize, elasticSearchResult.getSourceAsObjectList(GoodsSearchLine.class,false));

            //选择器数据：分类、品牌、参数
            Map<String, Object> selectorMap = new HashMap<>(16);
            MetricAggregation agg = elasticSearchResult.getAggregations();
            //分类
            TermsAggregation categoryAgg =  agg.getTermsAggregation("categoryAgg");
            List<TermsAggregation.Entry> categoryBuckets = categoryAgg.getBuckets();
            List<CategoryVO> allCatList = this.categoryManager.listAllChildren(0L);
            List<SearchSelector> catDim = SelectorUtil.createCatSelector(categoryBuckets, allCatList, goodsSearch.getCategory());
            selectorMap.put("cat", catDim);

            String catPath = null;
            if (goodsSearch.getCategory() != null) {
                CategoryDO cat = categoryManager.getModel(goodsSearch.getCategory());
                String path = cat.getCategoryPath();
                catPath = path.replace("|", Separator.SEPARATOR_PROP_VLAUE).substring(0, path.length() - 1);
            }

            //已经选择的分类
            List<SearchSelector> selectedCat = CatUrlUtils.getCatDimSelected(allCatList, catPath);
            selectorMap.put("selected_cat", selectedCat);

            //品牌
            TermsAggregation brandAgg =  agg.getTermsAggregation("brandAgg");
            List<TermsAggregation.Entry> brandBuckets = brandAgg.getBuckets();
            List<BrandDO> brandList = brandManager.getAllBrands();
            List<SearchSelector> brandDim = SelectorUtil.createBrandSelector(brandBuckets, brandList);
            selectorMap.put("brand", brandDim);

            //参数
            FilterAggregation paramsAgg =  agg.getFilterAggregation("paramsAgg");
            TermsAggregation nameTerms = paramsAgg.getTermsAggregation("nameAgg");
            List<PropSelector> paramDim = SelectorUtil.createParamSelector(nameTerms);
            selectorMap.put("prop", paramDim);

            goodsSearchResult.setGoodsData(webPage);
            goodsSearchResult.setSelectorData(selectorMap);

        } catch (Exception e) {
            goodsSearchResult.setGoodsData(new WebPage(pageNo, 0L, pageSize, new ArrayList()));
            goodsSearchResult.setSelectorData(new HashMap());
            e.printStackTrace();
        }

        return goodsSearchResult;
    }


    /**
     * 构建查询条件
     *
     * @return
     * @throws Exception
     */
    protected SearchSourceBuilder createQuery(GoodsSearchDTO goodsSearch) throws Exception {
        String keyword = goodsSearch.getKeyword();
        Long cat = goodsSearch.getCategory();
        Long brand = goodsSearch.getBrand();
        String price = goodsSearch.getPrice();
        Long sellerId = goodsSearch.getSellerId();
        Long shopCatId = goodsSearch.getShopCatId();
        //sourceBuilder 是用来构建查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 关键字检索
        if (!StringUtil.isEmpty(keyword)) {
            Map<String, Float> fields = new HashMap<>();
            fields.put("name", QueryStringQueryBuilder.DEFAULT_BOOST);
            fields.put("name.keyword", QueryStringQueryBuilder.DEFAULT_BOOST);

            QueryStringQueryBuilder queryString = new QueryStringQueryBuilder(keyword).field("name").fields(fields);
            queryString.defaultOperator(Operator.AND);

            //按照关键字检索  关键字无需按照最细粒度分词  update by liuyulei 2019-12-12
            queryString.analyzer("ik_smart");
            boolQueryBuilder.must(queryString);


        }
        // 品牌搜素
        if (brand != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("brand", brand));
        }
        // 分类检索
        if (cat != null) {

            CategoryDO category = categoryManager.getModel(cat);
            if (category == null) {
                throw new ServiceException("", "该分类不存在");
            }

            boolQueryBuilder.must(QueryBuilders.wildcardQuery("categoryPath", HexUtils.encode(category.getCategoryPath()) + "*"));
        }
        //卖家搜索
        if (sellerId != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("sellerId", sellerId));
        }
        // 卖家分组搜索
        if (shopCatId != null) {
            ShopCatDO shopCat = shopCatClient.getModel(shopCatId);
            if (shopCat == null) {
                throw new ServiceException("", "该分组不存在");
            }
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("shopCatPath", HexUtils.encode(shopCat.getCatPath()) + "*"));
        }

        // 参数检索
        String prop = goodsSearch.getProp();
        if (!StringUtil.isEmpty(prop)) {
            String[] propArray = prop.split(Separator.SEPARATOR_PROP);
            for (String p : propArray) {
                String[] onpropAr = p.split(Separator.SEPARATOR_PROP_VLAUE);
                String name = onpropAr[0];
                String value = onpropAr[1];
                boolQueryBuilder.must(QueryBuilders.nestedQuery("params", QueryBuilders.termQuery("params.name", name), ScoreMode.None));
                boolQueryBuilder.must(QueryBuilders.nestedQuery("params", QueryBuilders.termQuery("params.value", value), ScoreMode.None));
            }
        }

        //价格搜索
        if (!StringUtil.isEmpty(price)) {
            String[] pricear = price.split(Separator.SEPARATOR_PROP_VLAUE);
            double min = StringUtil.toDouble(pricear[0], 0.0);
            double max = Integer.MAX_VALUE;

            if (pricear.length == 2) {
                max = StringUtil.toDouble(pricear[1], Double.MAX_VALUE);
            }
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").from(min).to(max).includeLower(true).includeUpper(true));
        }

        // 删除的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("disabled", "1"));
        // 未上架的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("marketEnable", "1"));
        // 待审核和审核不通过的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("isAuth", "1"));

        sourceBuilder.query(boolQueryBuilder);

        //排序
        String sortField = goodsSearch.getSort();

        String sortId = "priority";

        SortOrder sort = SortOrder.DESC;

        if (!StringUtil.isEmpty(sortField)) {

            Map<String, String> sortMap = SortContainer.getSort(sortField);

            sortId = sortMap.get("id");

            // 如果是默认排序  --默认排序根据 商品优先级排序  add by liuyulei _2019-07-01
            if ("def".equals(sortId)) {
                sortId = "priority";
            }
            if ("buynum".equals(sortId)) {
                sortId = "buyCount";
            }

            if ("desc".equals(sortMap.get("def_sort"))) {
                sort = SortOrder.DESC;
            } else {
                sort = SortOrder.ASC;
            }
        }


        sourceBuilder.sort(sortId, sort);

        //好平率
        if ("grade".equals(sortId)) {
            sourceBuilder.sort("commentNum", SortOrder.DESC);
            sourceBuilder.sort("buyCount", SortOrder.DESC);
        }

        //如果不是默认排序 则在原有搜索结果基础上加上商品优先级排序   add by liuyulei 2019-07-01
        if(!"priority".equals(sortId)){
            //商品优先级
            sourceBuilder.sort("priority",SortOrder.DESC);
        }



        return sourceBuilder;


    }

    @Override
    public List<GoodsWords> getGoodsWords(String keyword) {

        return this.goodsWordsMapper.getGoodsWords("%" + keyword + "%");
    }

    @Override
    public WebPage recommendGoodsList(GoodsSearchDTO goodsSearch) {
        List<HotKeyword> hotKeywords = hotkeywordClient.listByNum(1);
        String keywords = "";
        if(StringUtil.isNotEmpty(hotKeywords)){
            keywords = hotKeywords.get(0).getHotName();
        }
        goodsSearch.setKeyword(keywords);
        return searchGoodsAndSelector(goodsSearch).getGoodsData();
    }
}
